/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file uart_driver.c
 * @brief This file defines uart driver functions
 */

/*******Includes***************************************************************/
#include <string.h>
#include "uart_reg_access.h"
#include "clock_drv.h"
#include "dma_drv.h"

/*******Definitions************************************************************/


/*******Variables**************************************************************/
/*! @brief Table to save register addresses of uart instances */
static UART_Type* const s_uartRegBase[CONFIG_UART_INSTANCE_COUNT] = CONFIG_UART_REG_BASE_PTRS;

/*! @brief Pointer to uart runtime state structure */
static uart_state_t* s_uartState[CONFIG_UART_INSTANCE_COUNT] = {NULL};

/*! @brief Table to uart irq handler process functions */
static uart_irqhandler_process_t s_uartIrqHandlerProcess[CONFIG_UART_INSTANCE_COUNT] = {NULL};

/*! @brief Table to save UART clock names */
static const clk_names_t s_uartClkName[CONFIG_UART_INSTANCE_COUNT] = CONFIG_UART_CLOCK_NAMES;

/*! @brief Table to save UART module names */
static const module_names_t s_uartModuleName[CONFIG_UART_INSTANCE_COUNT] = CONFIG_UART_MODULE_NAMES;

/*! @brief Table to interrupt request ids */
static const IRQn_Type s_uartIrqId[CONFIG_UART_INSTANCE_COUNT] = CONFIG_UART_IRQS;

/*! @brief Table to dma peripheral request id for uart tx */
static const dma_perial_request_t s_uartTxDmaRequest[CONFIG_UART_INSTANCE_COUNT] = CONFIG_UART_TX_DMA_REQS;

/*! @brief Table to dma peripheral request id for uart rx */
static const dma_perial_request_t s_uartRxDmaRequest[CONFIG_UART_INSTANCE_COUNT] = CONFIG_UART_RX_DMA_REQS;

/*******Prototypes*************************************************************/
static void UART_StopTxUsingDma(uint32_t instance);
static void UART_StopTxUsingInt(uint32_t instance);
static void UART_StopSendData(uint32_t instance);
static void UART_RxDmaCallback(uint8_t channel, dma_event_t event, void* param);
static void UART_PutData(uint32_t instance);

static void UART_StopRxUsingDma(uint32_t instance);
static void UART_StopReceiveData(uint32_t instance);
static void UART_StopRxUsingInt(uint32_t instance);
static void UART_TxDmaCallback(uint8_t channel, dma_event_t event, void* param);
static void UART_GetData(uint32_t instance);

static void UART_IRQHandlerDefaultProcess(uint32_t instance);
static void UART_IRQHandlerTxEmptyProcess(uint32_t instance);
static void UART_IRQHandlerTxCompleteProcess(uint32_t instance);
static void UART_IRQHandlerRxProcess(uint32_t instance);
static void UART_IRQHandlerRxErrorProcess(uint32_t instance);

/*******Private Functions******************************************************/

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : UART_GetDefaultConfig
 *** Description   : Get the UART configuration structure with default values
 ******************************************************************************/
void UART_GetDefaultConfig(uart_config_t* uartConfig)
{
    ASSERT_PARAM(uartConfig != NULL);

    uartConfig->baudRate = 115200U;
    uartConfig->oversampleRate = UART_OVERSAMPLE_16;
    uartConfig->wordLength = UART_WORD_LEN_8BIT;
    uartConfig->parityMode = UART_PARITY_DISABLED;
    uartConfig->stopBitCount = UART_ONE_STOP_BIT;
    uartConfig->transferType = UART_USING_INTERRUPT;
    uartConfig->txDMAChannel = 0U;
    uartConfig->rxDMAChannel = 0U;
    uartConfig->rxIdleIntEnable = false;
    uartConfig->callback = NULL;
    uartConfig->callbackParam = NULL;
}

/*******************************************************************************
 *** Function Name : UART_Init
 *** Description   : Initializes an UART operation instance
 ******************************************************************************/
errcode_t UART_Init(uint32_t instance,
                    uart_state_t* uartState,
                    const uart_config_t* uartConfig
                   )
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    ASSERT_PARAM(uartState != NULL);
    ASSERT_PARAM(uartConfig != NULL);

    UART_Type* uartBase = s_uartRegBase[instance];
    module_names_t instanceModuleName = s_uartModuleName[instance];
    errcode_t errCode = ERR_SUCCESS;

    /* Enable uart module bus clock */
    CLOCK_SetModuleBusClockEnable(instanceModuleName, true);

    /* Set soft reset inactive of uart module */
    CLOCK_SetModuleSoftResetState(instanceModuleName, RESET_INACTIVE);

    NVIC_EnableIRQ(s_uartIrqId[instance]);

    memset(uartState, 0U, sizeof(uart_state_t));
    uartState->oversampleRate = uartConfig->oversampleRate;
    uartState->wordLength = uartConfig->wordLength;
    uartState->transferType = uartConfig->transferType;
    uartState->txDMAChannel = uartConfig->txDMAChannel;
    uartState->rxDMAChannel = uartConfig->rxDMAChannel;
    uartState->callback = uartConfig->callback;
    uartState->callbackParam = uartConfig->callbackParam;

    uartState->transmitStatus = ERR_SUCCESS;
    uartState->receiveStatus = ERR_SUCCESS;
    s_uartState[instance] = uartState;

    UART_REG_SetWordLength(uartBase, uartConfig->wordLength);
    UART_REG_SetParityModel(uartBase, uartConfig->parityMode);
    UART_REG_SetStopBitCount(uartBase, uartConfig->stopBitCount);
    if (uartConfig->rxIdleIntEnable) {
        UART_REG_SetRxIdleEnable(uartBase, true);
        UART_REG_SetIntEnable(uartBase, UART_INT_IDLE, true);
    }
    /* Register the default irq handler process function for uart instance */
    UART_InstallIRQHandlerProcess(instance, UART_IRQHandlerDefaultProcess);

    errCode = UART_SetBaudRate(instance, uartConfig->baudRate);

    /* Create the synchronization objects */
    if (errCode == ERR_SUCCESS) {
        errCode = OS_Adapter_CreateSemaphore(&uartState->txComplete, 0U);
    }
    if (errCode == ERR_SUCCESS) {
        errCode = OS_Adapter_CreateSemaphore(&uartState->rxComplete, 0U);
    }
    OS_Adapter_Init(NULL);
    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_Deinit
 *** Description   : Deinitializes an UART operation instance
 ******************************************************************************/
void UART_Deinit(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    UART_Type* uartBase = s_uartRegBase[instance];
    module_names_t instanceModuleName = s_uartModuleName[instance];

    if (uartBase && s_uartState[instance] && s_uartState[instance]->txComplete) {
        (void)OS_Adapter_DestroySemaphore(&s_uartState[instance]->txComplete);
        /* to fix the GCC compile warning, use the macro to distinguish */
#ifdef USING_OS_FREERTOS
        s_uartState[instance]->txComplete = NULL;
        if (s_uartState[instance]->rxComplete != NULL) {
#endif
            (void)OS_Adapter_DestroySemaphore(&s_uartState[instance]->rxComplete);
#ifdef USING_OS_FREERTOS
            s_uartState[instance]->rxComplete = NULL;
        }
#endif
    }

    /* Set soft reset active of uart module */
    CLOCK_SetModuleSoftResetState(instanceModuleName, RESET_ACTIVE);

    /* Disable uart module bus clock */
    CLOCK_SetModuleBusClockEnable(instanceModuleName, false);

    NVIC_DisableIRQ(s_uartIrqId[instance]);
    NVIC_ClearPendingIRQ(s_uartIrqId[instance]);

    s_uartState[instance] = NULL;
}

/*******************************************************************************
 *** Function Name : UART_InstallCallback
 *** Description   : Registers the callback function and the parameter for uart instance
 ******************************************************************************/
void UART_InstallCallback(uint32_t instance,
                          uart_callback_t callback,
                          void* param)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    ASSERT_PARAM(callback != NULL);

    s_uartState[instance]->callback = callback;
    s_uartState[instance]->callbackParam = param;
}

/*******************************************************************************
 *** Function Name : UART_SendDataBlocking
 *** Description   : Send data in a blocking way with a set timeout
 ******************************************************************************/
errcode_t UART_SendDataBlocking(uint32_t instance,
                                const uint8_t* txBuff,
                                uint32_t txSize,
                                uint32_t timeout)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    uart_state_t* uartState = s_uartState[instance];
    ASSERT_PARAM(uartState != NULL);
    uartState->isTxBlocking = true;

    if (UART_SendData(instance, txBuff, txSize) == ERR_SUCCESS) {
        if (OS_Adapter_WaitSemaphore(&uartState->txComplete, timeout) != ERR_SUCCESS) {
            uartState->isTxBlocking = false;
            uartState->transmitStatus = ERR_TIMEOUT;
            uartState->transferType == UART_USING_INTERRUPT ? UART_StopTxUsingInt(instance)
            : UART_StopTxUsingDma(instance);
        }
    }

    return uartState->transmitStatus;
}

/*******************************************************************************
 *** Function Name : UART_SendDataPolling
 *** Description   : Send data in polling way, wait for tx to be empty and fill in data
 ******************************************************************************/
errcode_t UART_SendDataPolling(uint32_t instance,
                               const uint8_t* txBuff,
                               uint32_t txSize)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    ASSERT_PARAM(txBuff != NULL);
    ASSERT_PARAM(txSize > 0U);

    uart_state_t* uartState = s_uartState[instance];
    ASSERT_PARAM(uartState != NULL);
    UART_Type* regBase = s_uartRegBase[instance];
    errcode_t errCode = ERR_SUCCESS;

    /* When the word length is 9bits, the transmission length must be an even number */
    if ((uartState->wordLength == UART_WORD_LEN_9BIT) && ((txSize & 1U) == 1U)) {
        errCode = ERR_ERROR;
    } else if (uartState->isTxBusy) {
        errCode = ERR_BUSY;
    } else {
        UART_REG_SetTxEnable(regBase, true);
        while (txSize > 0U) {
            /* Wait for the tx data fifo to be filled when it is not full */
            while (UART_REG_GetStatusFlag(regBase, UART_TX_FULL));
            uartState->txBuff = txBuff;
            UART_PutData(instance);
            if (uartState->wordLength == UART_WORD_LEN_9BIT) {
                txBuff += 2U;
                txSize -= 2U;
            } else {
                txBuff++;
                txSize--;
            }
        }
        /* Wait for tx data transmission to complete */
        while (!UART_REG_GetStatusFlag(regBase, UART_TX_COMPLETE));
        UART_REG_SetTxEnable(regBase, false);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_SendData
 *** Description   : Send data in a non-blocking way, the function returns immediately
 ******************************************************************************/
errcode_t UART_SendData(uint32_t instance,
                        const uint8_t* txBuff,
                        uint32_t txSize)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    ASSERT_PARAM(txBuff != NULL);
    ASSERT_PARAM(txSize > 0U);

    uart_state_t* uartState = s_uartState[instance];
    ASSERT_PARAM(uartState != NULL);
    UART_Type* regBase = s_uartRegBase[instance];
    errcode_t errCode = ERR_SUCCESS;

    /* When the word length is 9bits, the transmission length must be an even number */
    if ((uartState->wordLength == UART_WORD_LEN_9BIT) && ((txSize & 1U) == 1U)) {
        errCode = ERR_ERROR;
    } else if (uartState->isTxBusy) {
        errCode = ERR_BUSY;
        /* When using DMA, txsize cannot exceed the DMA configurable maximum length, and the driver does not support 9bit word length */
    } else if (uartState->transferType == UART_USING_DMA
               && (txSize > DMA_CHANNEL_TRANFER_MAX_SIZE || uartState->wordLength == UART_WORD_LEN_9BIT)) {
        errCode = ERR_UNSUPPORTED;
    } else {
        uartState->isTxBusy = true;
        uartState->txSize = txSize;
        uartState->txBuff = txBuff;
        uartState->transmitStatus = ERR_BUSY;
        if (uartState->transferType == UART_USING_INTERRUPT) {
            UART_REG_SetIntEnable(regBase, UART_INT_TX_EMPTY, true);
        } else if (uartState->transferType == UART_USING_DMA) {
            UART_REG_SetTxDMAEnable(regBase, true);
            DMA_ConfigSingleTransfer(uartState->txDMAChannel, DMA_TRANSFER_MEM2PERIPH,
                                     (uint32_t)txBuff, UART_REG_GetDataAddress(regBase),
                                     DMA_TRANSFER_WIDTH_8BIT, txSize, s_uartTxDmaRequest[instance]);
            DMA_InstallCallback(uartState->txDMAChannel, UART_TxDmaCallback, (void*)(instance));
            DMA_ConfigureInt(uartState->txDMAChannel, DMA_CHN_ALL_INT, true);
            DMA_StartChannel(uartState->txDMAChannel);
        } else {

        }
        UART_REG_SetTxEnable(regBase, true);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_AbortSendingData
 *** Description   : Abort sending data
 ******************************************************************************/
void UART_AbortSendingData(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    uart_state_t* uartState = s_uartState[instance];
    ASSERT_PARAM(uartState != NULL);
    /* If no data is currently send, return success directly */
    if (uartState->isTxBusy) {
        uartState->transmitStatus = ERR_UART_ABORTED;
        uartState->transferType == UART_USING_INTERRUPT ? UART_StopTxUsingInt(instance)
        : UART_StopTxUsingDma(instance);
    }
}

/*******************************************************************************
 *** Function Name : UART_GetTransmitStatus
 *** Description   : Get the current transfer status
 ******************************************************************************/
errcode_t UART_GetTransmitStatus(uint32_t instance, uint32_t* bytesRemaining)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    ASSERT_PARAM(bytesRemaining != NULL);

    uart_state_t* uartState = s_uartState[instance];
    ASSERT_PARAM(uartState != NULL);
    if (uartState->isTxBusy) {
        *bytesRemaining = (uartState->transferType == UART_USING_INTERRUPT
                           ? uartState->txSize
                           : DMA_GetRemainingCount(uartState->txDMAChannel));
    } else {
        *bytesRemaining = 0U;
    }

    return uartState->transmitStatus;
}



/*******************************************************************************
 *** Function Name : UART_ReceiveDataBlocking
 *** Description   : Receive data in a blocking way with a set timeout
 ******************************************************************************/
errcode_t UART_ReceiveDataBlocking(uint32_t instance,
                                   uint8_t* rxBuff,
                                   uint32_t rxSize,
                                   uint32_t timeout)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    uart_state_t* uartState = s_uartState[instance];
    ASSERT_PARAM(uartState != NULL);
    uartState->isRxBlocking = true;

    if (UART_ReceiveData(instance, rxBuff, rxSize) == ERR_SUCCESS) {
        if (OS_Adapter_WaitSemaphore(&uartState->rxComplete, timeout) != ERR_SUCCESS) {
            uartState->isRxBlocking = false;
            uartState->receiveStatus = ERR_TIMEOUT;
            uartState->transferType == UART_USING_INTERRUPT ? UART_StopRxUsingInt(instance)
            : UART_StopRxUsingDma(instance);
        }
    }

    return uartState->receiveStatus;
}

/*******************************************************************************
 *** Function Name : UART_ReceiveDataPolling
 *** Description   : Receive data in polling way, wait for rx to be non-empty and fill in data
 ******************************************************************************/
errcode_t UART_ReceiveDataPolling(uint32_t instance,
                                  uint8_t* rxBuff,
                                  uint32_t rxSize)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    ASSERT_PARAM(rxBuff != NULL);
    ASSERT_PARAM(rxSize > 0U);

    uart_state_t* uartState = s_uartState[instance];
    ASSERT_PARAM(uartState != NULL);
    UART_Type* regBase = s_uartRegBase[instance];
    errcode_t errCode = ERR_SUCCESS;

    /* When the word length is 9bits, the receive length must be an even number */
    if ((uartState->wordLength == UART_WORD_LEN_9BIT) && ((rxSize & 1U) == 1U)) {
        errCode = ERR_ERROR;
    } else if (uartState->isRxBusy) {
        errCode = ERR_BUSY;
    } else {
        UART_REG_SetRxEnable(regBase, true);
        while (rxSize > 0U && errCode == ERR_SUCCESS) {
            /* Wait for the rx data register not to be empty */
            while (!UART_REG_GetStatusFlag(regBase, UART_RX_NOT_EMPTY));
            uartState->rxBuff = rxBuff;
            UART_GetData(instance);
            if (uartState->wordLength == UART_WORD_LEN_9BIT) {
                rxBuff += 2U;
                rxSize -= 2U;
            } else {
                rxBuff++;
                rxSize--;
            }

            if (UART_REG_GetStatusFlag(regBase, UART_FRAME_ERROR)) {
                errCode = ERR_UART_FRAMING_ERROR;
            } else if (UART_REG_GetStatusFlag(regBase, UART_PARITY_ERROR)) {
                errCode = ERR_UART_PARITY_ERROR;
            } else if (UART_REG_GetStatusFlag(regBase, UART_NOSIE_ERROR)) {
                errCode = ERR_UART_NOISE_ERROR;
            } else if (UART_REG_GetStatusFlag(regBase, UART_RX_OVERFLOW)) {
                errCode = ERR_UART_RX_OVERRUN;
            }
        }

        UART_REG_SetRxEnable(regBase, false);
        if (errCode != ERR_SUCCESS) {
            UART_REG_ClearErrorFlags(regBase);
        }

        /* If all data has been received, but overflow occurs at this time,
           it also considered to be successful */
        if (rxSize == 0 && errCode == ERR_UART_RX_OVERRUN) {
            errCode = ERR_SUCCESS;
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_ReceiveData
 *** Description   : Receive data in a non-blocking way, the function returns immediately
 ******************************************************************************/
errcode_t UART_ReceiveData(uint32_t instance,
                           uint8_t* rxBuff,
                           uint32_t rxSize)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    ASSERT_PARAM(rxBuff != NULL);
    ASSERT_PARAM(rxSize > 0U);

    uart_state_t* uartState = s_uartState[instance];
    ASSERT_PARAM(uartState != NULL);
    errcode_t errCode = ERR_SUCCESS;

    if (uartState->isRxBusy) {
        errCode = ERR_BUSY;
        /* When the word length is 9bits, the receive length must be an even number */
    } else if ((uartState->wordLength == UART_WORD_LEN_9BIT) && ((rxSize & 1U) != 0U)) {
        errCode = ERR_ERROR;
        /* When using DMA, rxsize cannot exceed the DMA configurable maximum length, and the driver does not support 9bit word length */
    } else if (uartState->transferType == UART_USING_DMA
               && (rxSize > DMA_CHANNEL_TRANFER_MAX_SIZE || uartState->wordLength == UART_WORD_LEN_9BIT)) {
        errCode = ERR_UNSUPPORTED;
    } else {
        uartState->isRxBusy = true;
        uartState->rxSize = rxSize;
        uartState->rxBuff = rxBuff;
        uartState->receiveStatus = ERR_BUSY;

        UART_Type* regBase = s_uartRegBase[instance];
        if (uartState->transferType == UART_USING_INTERRUPT) {
            UART_REG_SetIntEnable(regBase, UART_INT_RX_NOT_EMPTY, true);
        } else if (uartState->transferType == UART_USING_DMA) {
            UART_REG_SetRxDMAEnable(regBase, true);
            DMA_ConfigSingleTransfer(uartState->rxDMAChannel, DMA_TRANSFER_PERIPH2MEM,
                                     (uint32_t)(&regBase->DATA), (uint32_t)rxBuff,
                                     DMA_TRANSFER_WIDTH_8BIT, rxSize, s_uartRxDmaRequest[instance]);
            DMA_InstallCallback(uartState->rxDMAChannel, UART_RxDmaCallback, (void*)(instance));
            DMA_ConfigureInt(uartState->rxDMAChannel, DMA_CHN_ALL_INT, true);
            DMA_StartChannel(uartState->rxDMAChannel);
        }

        UART_REG_SetRxEnable(regBase, true);
        UART_REG_SetErrorInts(regBase, true);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_ReceiveDataCyclically
 *** Description   : Receive data in a cyclical way by using DMA loop mode, the function returns immediately
 ******************************************************************************/
errcode_t UART_ReceiveDataCyclically(uint32_t instance,
                                     uint8_t* rxBuff,
                                     uint32_t rxSize)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    ASSERT_PARAM(rxBuff != NULL);
    ASSERT_PARAM(rxSize > 0U);

    uart_state_t* uartState = s_uartState[instance];
    ASSERT_PARAM(uartState != NULL);
    errcode_t errCode = ERR_SUCCESS;
    UART_Type* regBase = s_uartRegBase[instance];

    if (uartState->isRxBusy) {
        errCode = ERR_BUSY;
        /* When the word length is 9bits, the receive length must be an even number */
    } else if ((uartState->wordLength == UART_WORD_LEN_9BIT) && ((rxSize & 1U) != 0U)) {
        errCode = ERR_ERROR;
        /* When using DMA, rxsize cannot exceed the DMA configurable maximum length, and the driver does not support 9bit word length */
    } else if ((uartState->transferType != UART_USING_DMA)
               || (rxSize > DMA_CHANNEL_TRANFER_MAX_SIZE) || (uartState->wordLength == UART_WORD_LEN_9BIT)) {
        errCode = ERR_UNSUPPORTED;
    } else {
        uartState->isRxBusy = true;
        uartState->rxSize = rxSize;
        uartState->rxBuff = rxBuff;
        uartState->receiveStatus = ERR_BUSY;

        UART_REG_SetRxDMAEnable(regBase, true);
        DMA_ConfigLoopTransfer(uartState->rxDMAChannel, DMA_TRANSFER_PERIPH2MEM,
                               (uint32_t)(&regBase->DATA), (uint32_t)rxBuff,
                               DMA_TRANSFER_WIDTH_8BIT, rxSize, s_uartRxDmaRequest[instance]);
        DMA_InstallCallback(uartState->rxDMAChannel, UART_RxDmaCallback, (void*)(instance));
        DMA_ConfigureInt(uartState->rxDMAChannel, DMA_CHN_ERROR_INT, true);
        DMA_StartChannel(uartState->rxDMAChannel);

        UART_REG_SetRxEnable(regBase, true);
        UART_REG_SetErrorInts(regBase, true);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_GetReceiveStatus
 *** Description   : Get the current receive status
 ******************************************************************************/
errcode_t UART_GetReceiveStatus(uint32_t instance, uint32_t* bytesRemaining)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    ASSERT_PARAM(bytesRemaining != NULL);

    uart_state_t* uartState = s_uartState[instance];
    ASSERT_PARAM(uartState != NULL);
    if (uartState->isRxBusy) {
        *bytesRemaining = uartState->transferType == UART_USING_INTERRUPT
                          ? uartState->rxSize
                          : DMA_GetRemainingCount(uartState->rxDMAChannel);
        /* Note: When the return is ERR_SUCCESS, the value of byteRemaining may be 1.
           This is because after bytesRemaining is assigned, it may enter the interrupt,
           and the value of receiveStatus becomes ERR_SUCCESS, so return value is ERR_SUCCESS. */
    } else {
        *bytesRemaining = 0U;
    }

    return uartState->receiveStatus;
}


/*******************************************************************************
 *** Function Name : UART_AbortReceivingData
 *** Description   : Abort receiving data
 ******************************************************************************/
void UART_AbortReceivingData(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    uart_state_t* uartState = s_uartState[instance];
    ASSERT_PARAM(uartState != NULL);
    if (uartState->isRxBusy) {
        uartState->receiveStatus = ERR_UART_ABORTED;
        uartState->transferType == UART_USING_INTERRUPT ? UART_StopRxUsingInt(instance)
        : UART_StopRxUsingDma(instance);
    }
}

/*******************************************************************************
 *** Function Name : UART_SetBaudRate
 *** Description   : Configures the uart baud rate
 ******************************************************************************/
errcode_t UART_SetBaudRate(uint32_t instance, uint32_t baudRate)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    uart_state_t* uartState = s_uartState[instance];

    ASSERT_PARAM(uartState != NULL);

    errcode_t errCode = ERR_ERROR;
    if (uartState->isTxBusy || uartState->isRxBusy) {
        errCode = ERR_BUSY;
    } else {
        clk_names_t instanceClkName = s_uartClkName[instance];
        uint32_t uartClock = 0U;
        if (CLOCK_GetFreq(instanceClkName, &uartClock) == ERR_SUCCESS) {
            /* Check if the desired baud rate can be configured with the current protocol clock. */
            ASSERT_PARAM(uartClock > 0U);

            UART_Type* uartRegBase = s_uartRegBase[instance];
            uart_oversample_rate_t overSampleRate = uartState->oversampleRate;
            uint8_t div = (overSampleRate == UART_OVERSAMPLE_16 ? 16U : 4U);
            if (uartClock / div >= baudRate) {
                UART_REG_SetOversamplingRate(uartRegBase, overSampleRate);
                /* Add 0.5 * overSampleRate * targetBaudRate for rounding the divisor */
                UART_REG_SetBaudRateDivisor(uartRegBase, ((uartClock << UART_BAUD_SCALED_SHIFT) + div * baudRate / 2) /
                                            div / baudRate);
                errCode = ERR_SUCCESS;
            }
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_GetBaudRate
 *** Description   : Get the uart baud rate
 ******************************************************************************/
void UART_GetBaudRate(uint32_t instance, uint32_t* baudRate)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    ASSERT_PARAM(baudRate != NULL);

    UART_Type* uartBase = s_uartRegBase[instance];
    clk_names_t instanceClkName = s_uartClkName[instance];
    uint32_t uartClock = 0U;
    (void)CLOCK_GetFreq(instanceClkName, &uartClock);

    ASSERT_PARAM(uartClock > 0U);

    uart_oversample_rate_t overSampleRate = UART_REG_GetOversamplingRate(uartBase);
    uint8_t div = (overSampleRate == UART_OVERSAMPLE_16 ? 16U : 4U);

    uint32_t baudDivisor = UART_REG_GetBaudRateDivisor(uartBase);

    *baudRate = (uartClock << UART_BAUD_SCALED_SHIFT) / div / baudDivisor;
}

/*******************************************************************************
 *** Function Name : UART_IRQHandlerProcess
 *** Description   : UART interrupt handler process
 ******************************************************************************/
void UART_IRQHandlerProcess(uint32_t instance)
{
    if (s_uartIrqHandlerProcess[instance] != NULL) {
        s_uartIrqHandlerProcess[instance](instance);
    }
}

/*******************************************************************************
 *** Function Name : UART_InstallIRQHandlerProcess
 *** Description   : Registers the irq handler process function for uart instance
 ******************************************************************************/
void UART_InstallIRQHandlerProcess(uint32_t instance, uart_irqhandler_process_t irqHandlerProcess)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);
    ASSERT_PARAM(irqHandlerProcess != NULL);

    s_uartIrqHandlerProcess[instance] = irqHandlerProcess;
}

/*******************************************************************************
 *** Function Name : UART_StopTxUsingDma
 *** Description   : Stop transfer data by DMA
 ******************************************************************************/
static void UART_StopTxUsingDma(uint32_t instance)
{
    uart_state_t* uartState = s_uartState[instance];
    UART_Type* regBase = s_uartRegBase[instance];

    UART_REG_SetTxDMAEnable(regBase, false);
    DMA_StopChannel(uartState->txDMAChannel);

    UART_StopSendData(instance);

}

/*******************************************************************************
 *** Function Name : UART_StopSendData
 *** Description   : Stop sending data
 ******************************************************************************/
static void UART_StopSendData(uint32_t instance)
{
    uart_state_t* uartState = s_uartState[instance];
    UART_Type* regBase = s_uartRegBase[instance];

    UART_REG_SetIntEnable(regBase, UART_INT_TX_COMPLETE, false);
    UART_REG_SetTxEnable(regBase, false);

    if (uartState->isTxBlocking) {
        (void)OS_Adapter_PostSemaphore(&uartState->txComplete);
        uartState->isTxBlocking = false;
    }

    if (uartState->transmitStatus == ERR_BUSY) {
        uartState->transmitStatus = ERR_SUCCESS;
    }

    uartState->isTxBusy = false;
}

/*******************************************************************************
 *** Function Name : UART_StopTxUsingInt
 *** Description   : Stop receiving data by interrupt
 ******************************************************************************/
static void UART_StopTxUsingInt(uint32_t instance)
{
    uart_state_t* uartState = s_uartState[instance];
    UART_Type* regBase = s_uartRegBase[instance];

    if (uartState->transmitStatus != ERR_BUSY) {
        /* If the transfer is aborted or timed out, disable tx empty interrupt */
        UART_REG_SetIntEnable(regBase, UART_INT_TX_EMPTY, false);
    }

    UART_StopSendData(instance);
}

/*******************************************************************************
 *** Function Name : UART_TxDmaCallback
 *** Description   : Tx dma callback function
 ******************************************************************************/
static void UART_TxDmaCallback(uint8_t channel, dma_event_t event, void* param)
{
    (void)channel;
    uint32_t instance = ((uint32_t)param);
    uart_state_t* uartState = s_uartState[instance];
    UART_Type* regBase = s_uartRegBase[instance];

    if (event == DMA_EVENT_TRANFER_COMPLETE) {
        uartState->txSize = 0U;
        if (uartState->callback) {
            uartState->callback(instance, UART_EVENT_TX_EMPTY, uartState->callbackParam);
        }
        UART_REG_SetIntEnable(regBase, UART_INT_TX_COMPLETE, true);
    } else {
        uartState->transmitStatus = ERR_ERROR;
        UART_StopTxUsingDma(instance);
        if (uartState->callback) {
            uartState->callback(instance, UART_EVENT_ERROR, uartState->callbackParam);
        }
    }
}


/*******************************************************************************
 *** Function Name : UART_PutData
 *** Description   : Put data to tx data register
 ******************************************************************************/
static void UART_PutData(uint32_t instance)
{
    uart_state_t* uartState = s_uartState[instance];
    UART_Type* regBase = s_uartRegBase[instance];
    const uint8_t* txBuff = uartState->txBuff;
    while (UART_REG_GetStatusFlag(regBase, UART_TX_FULL));
    if (uartState->wordLength == UART_WORD_LEN_9BIT) {
        uint16_t data = (uint16_t)(*txBuff);
        txBuff++;
        data |= (uint16_t)(((uint16_t)(*txBuff)) << 8U);
        UART_REG_PutData(regBase, data);
    } else {
        UART_REG_PutData(regBase, *txBuff);
    }
}

/*******************************************************************************
 *** Function Name : UART_GetData
 *** Description   : Get data from rx data register
 ******************************************************************************/
static void UART_GetData(uint32_t instance)
{
    uart_state_t* uartState = s_uartState[instance];
    UART_Type* regBase = s_uartRegBase[instance];
    uint8_t* rxBuff = uartState->rxBuff;
    uint16_t data = UART_REG_GetData(regBase);

    *rxBuff = (uint8_t)(data & 0xFFU);
    if (uartState->wordLength == UART_WORD_LEN_9BIT) {
        rxBuff++;
        /* Write the ninth bit to the subsequent byte in the rx buffer */
        *rxBuff = (uint8_t)(data >> 8U);
    }
}

/*******************************************************************************
 *** Function Name : UART_IRQHandlerTxEmptyProcess
 *** Description   : UART handling rx empty process
 ******************************************************************************/
static void UART_IRQHandlerTxEmptyProcess(uint32_t instance)
{
    uart_state_t* uartState = s_uartState[instance];
    UART_Type* regBase = s_uartRegBase[instance];
    uint8_t frameCount = 0U;

    /* The tx fifo of uart has a maximum depth, which can be filled with frames of data at once */
    while (uartState->txSize > 0U && frameCount < CONFIG_UART_TX_FIFO_DEPTH) {
        UART_PutData(instance);
        frameCount++;
        if (uartState->wordLength == UART_WORD_LEN_9BIT) {
            uartState->txBuff += 2U;
            uartState->txSize -= 2U;
        } else {
            uartState->txBuff++;
            uartState->txSize--;
        }
    }
    if (uartState->txSize == 0U) {
        if (uartState->callback) {
            uartState->callback(instance, UART_EVENT_TX_EMPTY, uartState->callbackParam);
        }
        UART_REG_SetIntEnable(regBase, UART_INT_TX_EMPTY, false);
        UART_REG_SetIntEnable(regBase, UART_INT_TX_COMPLETE, true);
    }
}

/*******************************************************************************
 *** Function Name : UART_IRQHandlerTxCompleteProcess
 *** Description   : UART handling rx complete process
 ******************************************************************************/
static void UART_IRQHandlerTxCompleteProcess(uint32_t instance)
{
    uart_state_t* uartState = s_uartState[instance];

    if (uartState->txSize == 0U) {
        uartState->transferType == UART_USING_INTERRUPT ? UART_StopTxUsingInt(instance)
        : UART_StopTxUsingDma(instance);

        if (uartState->callback) {
            uartState->callback(instance, UART_EVENT_END_TRANSFER, uartState->callbackParam);
        }
    }
}

/*******************************************************************************
 *** Function Name : UART_IRQHandlerRxProcess
 *** Description   : Deinitializes an UART operation instance
 ******************************************************************************/
static void UART_IRQHandlerRxProcess(uint32_t instance)
{
    uart_state_t* uartState = s_uartState[instance];

    if (uartState->rxSize > 0U) {
        UART_GetData(instance);
        if (uartState->wordLength == UART_WORD_LEN_9BIT) {
            uartState->rxBuff += 2U;
            uartState->rxSize -= 2U;
        } else {
            uartState->rxBuff++;
            uartState->rxSize--;
        }
    }
    if (uartState->rxSize == 0U) {
        UART_StopRxUsingInt(instance);
        if (uartState->callback) {
            uartState->callback(instance, UART_EVENT_RX_COMPLETE, uartState->callbackParam);
        }
    }
}

/*******************************************************************************
 *** Function Name : UART_IRQHandlerRxIdleProcess
 *** Description   : UART handling rx idle process
 ******************************************************************************/
static void UART_IRQHandlerRxIdleProcess(uint32_t instance)
{
    uart_state_t* uartState = s_uartState[instance];
    UART_Type* regBase = s_uartRegBase[instance];

    if (uartState->callback) {
        uartState->callback(instance, UART_EVENT_RX_IDLE, uartState->callbackParam);
    }

    UART_REG_ClearStatusFlag(regBase, UART_IDLE);
}

/*******************************************************************************
 *** Function Name : UART_IRQHandlerRxErrorProcess
 *** Description   : UART handling rx error process
 ******************************************************************************/
static void UART_IRQHandlerRxErrorProcess(uint32_t instance)
{
    uart_state_t* uartState = s_uartState[instance];
    UART_Type* regBase = s_uartRegBase[instance];

    if (UART_REG_GetStatusFlag(regBase, UART_RX_OVERFLOW)) {
        uartState->receiveStatus = ERR_UART_RX_OVERRUN;
    } else if (UART_REG_GetStatusFlag(regBase, UART_FRAME_ERROR)) {
        uartState->receiveStatus = ERR_UART_FRAMING_ERROR;
    } else if (UART_REG_GetStatusFlag(regBase, UART_PARITY_ERROR)) {
        uartState->receiveStatus = ERR_UART_PARITY_ERROR;
    } else if (UART_REG_GetStatusFlag(regBase, UART_NOSIE_ERROR)) {
        uartState->receiveStatus = ERR_UART_NOISE_ERROR;
    }


    uartState->transferType == UART_USING_INTERRUPT ? UART_StopRxUsingInt(instance)
    : UART_StopRxUsingDma(instance);

    if (uartState->callback) {
        uartState->callback(instance, UART_EVENT_ERROR, uartState->callbackParam);
    }

    UART_REG_ClearErrorFlags(regBase);
}

/*******************************************************************************
 *** Function Name : UART_RxDmaCallback
 *** Description   : Uart rx dma callback function
 ******************************************************************************/
static void UART_RxDmaCallback(uint8_t channel, dma_event_t event, void* param)
{
    (void)channel;
    uint32_t instance = ((uint32_t)param);
    uart_state_t* uartState = s_uartState[instance];
    UART_Type* regBase = s_uartRegBase[instance];

    if (event == DMA_EVENT_TRANFER_COMPLETE) {
        uartState->rxSize = 0U;
        UART_StopRxUsingDma(instance);
        if (uartState->callback) {
            uartState->callback(instance, UART_EVENT_RX_COMPLETE, uartState->callbackParam);
        }
    } else {
        uartState->receiveStatus = ERR_ERROR;
        UART_StopRxUsingDma(instance);
        if (uartState->callback) {
            uartState->callback(instance, UART_EVENT_ERROR, uartState->callbackParam);
        }
        UART_REG_ClearErrorFlags(regBase);
    }
}

/*******************************************************************************
 *** Function Name : UART_StopRxUsingDma
 *** Description   : Stop receiving data by dma
 ******************************************************************************/
static void UART_StopRxUsingDma(uint32_t instance)
{
    uart_state_t* uartState = s_uartState[instance];
    UART_Type* regBase = s_uartRegBase[instance];

    UART_REG_SetRxDMAEnable(regBase, false);
    DMA_StopChannel(uartState->rxDMAChannel);

    UART_StopReceiveData(instance);
}

/*******************************************************************************
 *** Function Name : UART_StopReceiveData
 *** Description   : Stop receiving data
 ******************************************************************************/
static void UART_StopReceiveData(uint32_t instance)
{
    uart_state_t* uartState = s_uartState[instance];
    UART_Type* regBase = s_uartRegBase[instance];

    UART_REG_SetErrorInts(regBase, false);
    UART_REG_SetRxEnable(regBase, false);

    if (uartState->isRxBlocking) {
        (void)OS_Adapter_PostSemaphore(&uartState->rxComplete);
        uartState->isRxBlocking = false;
    }

    if (uartState->receiveStatus == ERR_BUSY) {
        uartState->receiveStatus = ERR_SUCCESS;
    }

    uartState->isRxBusy = false;
}

/*******************************************************************************
 *** Function Name : UART_StopRxUsingInt
 *** Description   : Stop receiving data by interrupt
 ******************************************************************************/
static void UART_StopRxUsingInt(uint32_t instance)
{
    UART_Type* regBase = s_uartRegBase[instance];

    UART_REG_SetIntEnable(regBase, UART_INT_RX_NOT_EMPTY, false);
    UART_StopReceiveData(instance);
}


/*******************************************************************************
 *** Function Name : UART_IRQHandlerDefaultProcess
 *** Description   : UART interrupt default handler process
 ******************************************************************************/
static void UART_IRQHandlerDefaultProcess(uint32_t instance)
{
    UART_Type* regBase = s_uartRegBase[instance];

    if (UART_REG_IsIntEnabled(regBase, UART_INT_TX_EMPTY)
            && UART_REG_GetStatusFlag(regBase, UART_TX_EMPTY)) {
        UART_IRQHandlerTxEmptyProcess(instance);
    }

    if (UART_REG_IsIntEnabled(regBase, UART_INT_TX_COMPLETE)
            && UART_REG_GetStatusFlag(regBase, UART_TX_COMPLETE)) {
        UART_IRQHandlerTxCompleteProcess(instance);
    }

    if (UART_REG_IsIntEnabled(regBase, UART_INT_RX_NOT_EMPTY)
            && UART_REG_GetStatusFlag(regBase, UART_RX_NOT_EMPTY)) {
        UART_IRQHandlerRxProcess(instance);
    }

    if (UART_REG_IsIntEnabled(regBase, UART_INT_IDLE)
            && UART_REG_GetStatusFlag(regBase, UART_IDLE)) {
        UART_IRQHandlerRxIdleProcess(instance);
    }

    if (UART_REG_GetStatusFlag(regBase, UART_RX_OVERFLOW)
            || UART_REG_GetStatusFlag(regBase, UART_FRAME_ERROR)
            || UART_REG_GetStatusFlag(regBase, UART_PARITY_ERROR)
            || UART_REG_GetStatusFlag(regBase, UART_NOSIE_ERROR)) {
        UART_IRQHandlerRxErrorProcess(instance);
    }
}
